/*

    This file is part of Hometree.

    Hometree is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Hometree is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Hometree.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2011,2012 Roman Held

*/
/********************************************************************\
                            Includes
\********************************************************************/
#include <QDebug>
#include "htr/htrlog.h"
#include "htr/mansock.h"
#include "htr/htrpersistentservice.h"
#include "htrsmimedataresource.h"
#include "htrsplugins.h"

/********************************************************************\
                            Macros/Constants
\********************************************************************/
#define IDENT "SMDR"

/********************************************************************\
                            Implementation
\********************************************************************/
/*--------------------------------------------------------------------
*/
HtrsMimeDataResource::HtrsMimeDataResource(const QString &url)
 : HtrsResource(url, Htr::MimeDataType)
{
  mIsPersistent = false;
  mIsPermanent  = false;
  mWriteLevel = Htr::Res::NotOpen;
  mState      = Htr::Res::StUndefined;
}

/*--------------------------------------------------------------------
*/
HtrsMimeDataResource::~HtrsMimeDataResource()
{
}

/*--------------------------------------------------------------------
*/
bool HtrsMimeDataResource::open(qint32 openMode)
/*
   - Ressource erstellen, falls notwendig und gewunescht
   - ReadOnly ist immer moeglich
   - WriteOnly nur, wenn Ressource nicht Exclusiv besetzt ist
   - Exclusive nur, wenn Ressource nicht mit WriteOnly besetzt ist
*/
{
  // TODO: Konsistenzcheck der Flags?

  // Erstellen:
  if (mWriteLevel == Htr::Res::NotOpen) {
    if (openMode & Htr::Res::Create) {
      mWriteLevel = Htr::Res::ReadOnly;  // Erstellen der Ressource!
      if (openMode & Htr::Res::Persistent)
        mIsPersistent = true;
      else // not persistent? maybe we have to remove this resource:
        if (HtrsPlugins::instance()->persistentService())
          HtrsPlugins::instance()->persistentService()->clear(this);
      setState(Htr::Res::StReady);
    } else {
      HtrLog::instance()->warning(IDENT,QString("Resource '%1': Open-Request for closed resource").arg(url()));
      return false;                     // Ressource 
    }
  }


  // Pruefen des Zugriffslevels:
  if (openMode & Htr::Res::WriteOnly) {
    if (mWriteLevel == Htr::Res::WriteExclusive) // bereits besetzt
      return false;
    if (((openMode & Htr::Res::WriteExclusive) > 0) && 
        (mWriteLevel >= Htr::Res::WriteOnly)) 
      return false;
    
    if (openMode & Htr::Res::WriteExclusive)
      mWriteLevel = Htr::Res::WriteExclusive;
    else
      mWriteLevel = Htr::Res::WriteOnly;
  }
  
  // not first open, but first client with persistence-request?
  if ((openMode & Htr::Res::Persistent) && !mIsPersistent) {
        mIsPersistent = true;
    if (HtrsPlugins::instance()->persistentService())
      HtrsPlugins::instance()->persistentService()->store(this);
  }

  return true;
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::close(QObject *session)
{
  if (mFollowers.contains(session)) {
    HtrLog::instance()->info(IDENT,QString("Resource '%1': Follower removed").arg(url()));
    mFollowers.removeAll(session);
  }
  if (mFeeders.contains(session)) {
    HtrLog::instance()->info(IDENT,QString("Resource '%1': Feeder removed").arg(url()));
    mFeeders.removeAll(session);
    if (mFeeders.isEmpty())
      mWriteLevel = Htr::Res::NotOpen; 
  }

  if (mFeeders.isEmpty() && mFollowers.isEmpty() && !mIsPersistent && !mIsPermanent) {
    HtrLog::instance()->info(IDENT,QString("Resource '%1': about to delete").arg(url()));
    emit aboutToDelete();
    deleteLater(); 
  }
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::setMimeData(const QByteArray &data)
{
  if ((mState != Htr::Res::StReady) && (mState != Htr::Res::StUndefined)) {
    HtrLog::instance()->warning(IDENT,QString("MimeData rejected (URL=%1, State=%2)").arg(url()).arg(mState));
    return;
  }
  mMimeData = data;
  store();
  commitMimeData();
}
   
/*--------------------------------------------------------------------
*/
QByteArray HtrsMimeDataResource::mimeData() const
{
  return mMimeData;
}

/*--------------------------------------------------------------------
*/
void  HtrsMimeDataResource::setMimeType(const QString &mimeType)
{
  if (mimeType != mMimeType) {
    mMimeType = mimeType;
    store();
    commitMimeType();
  }
}

/*--------------------------------------------------------------------
*/
QString    HtrsMimeDataResource::mimeType() const
{
  return mMimeType;
}

/*--------------------------------------------------------------------
*/
void  HtrsMimeDataResource::setPermanent(bool isPermanent)
{
  mIsPermanent = isPermanent;
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::setState(Htr::Res::State state)
{
  if (state != mState) {
    mState = state;
    commitState();
  }
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::handleMsg(HtrManagedSocket *sock)
{
  Q_ASSERT(sock);
  QDataStream &inStream = sock->readStream();
  qint32 msg;
  inStream >> msg;
  switch (msg) {
    case Htr::Net::OpenMsg: {
        handleOpenMsg(sock);
      } break;
    case Htr::Net::CloseMsg: {
        handleCloseMsg(sock);
      } break;
    case Htr::Net::MimeDataMsg: {
        handleMimeDataMsg(sock);
      } break;
    case Htr::Net::MimeTypeMsg: {
        handleMimeTypeMsg(sock);
      } break;
  }
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::handleOpenMsg(HtrManagedSocket *sock)
{
  Q_ASSERT(sock);
  QDataStream &inStream = sock->readStream();
  qint32 openMode;
  
  inStream >> openMode;
  if (open(openMode)) {
    HtrLog::instance()->info(IDENT,QString("Resource '%1': (re)opend").arg(url()));
    // Registrierung:
    QObject *session = sock->parent();
    // TODO: besser aufgehoben in HtrsResource: ?
    connect(this, SIGNAL(sendBroadCast(const QByteArray&)),
             session, SLOT(resourceBroadCast(const QByteArray&)));
    connect(session, SIGNAL(destroyed(QObject*)),
            this, SLOT(close(QObject*))); 

    // Q_ASSERT(!mFeeders.contains(session));
    // Q_ASSERT(!mFollowers.contains(session));

    if ((openMode & Htr::Res::WriteOnly) && !mFeeders.contains(session))
      mFeeders << session;
    if ((openMode & Htr::Res::ReadOnly) && !mFollowers.contains(session))
      mFollowers << session;

    // Einmalige Update-Meldung
    QDataStream &outStream = sock->prepareStream();
    outStream << (qint32)Htr::Net::ClResourceMsg;
    outStream << id();
    outStream << (qint32)Htr::Net::UpdateMsg;
    outStream << mMimeType;
    outStream << mMimeData;
    sock->writeStream();
  } else {
    QString msg = QString("Resource '%1': (re)open failed (current level: %2, requested mode: %3)")
               .arg(url())
               .arg(mWriteLevel)
               .arg(openMode);
    HtrLog::instance()->warning(IDENT,msg);

    // TODO: Error-Unterscheidung..?
    sendError(Htr::Res::ErrOccupied,sock);
  }
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::handleCloseMsg(HtrManagedSocket *sock)
{
  Q_ASSERT(sock);
  QObject *session = sock->parent();
  Q_ASSERT(session);
  close(session);
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::handleMimeDataMsg(HtrManagedSocket *sock)
{
  if (mState != Htr::Res::StReady) {
    HtrLog::instance()->warning(IDENT,QString("MimeData rejected (URL=%1, State=%2)").arg(url()).arg(mState));
    return;
  }
  HtrLog::instance()->info(IDENT,QString("Resource '%1': MimeData changed").arg(url()));
  Q_ASSERT(sock);
  Q_ASSERT(mFeeders.contains(sock->parent()));
  QDataStream &inStream = sock->readStream();
  inStream >> mMimeData;
 
  store(); 
  commitMimeData();
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::handleMimeTypeMsg(HtrManagedSocket *sock)
{
  if (mState != Htr::Res::StReady) {
    HtrLog::instance()->warning(IDENT,QString("MimeData rejected (URL=%1, State=%2)").arg(url()).arg(mState));
    return;
  }
  HtrLog::instance()->info(IDENT,QString("Resource '%1': MimeType changed").arg(url()));
  Q_ASSERT(sock);
  Q_ASSERT(mFeeders.contains(sock->parent()));
  QDataStream &inStream = sock->readStream();
  inStream >> mMimeType;

  store(); 
  commitMimeType();
}


/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::commitState()
{
  emit stateChanged(this, mState);
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::commitMimeData()
{
  QByteArray  outPaket;
  QDataStream outStream(&outPaket,QIODevice::WriteOnly);
  HtrManagedSocket::setupStream(outStream);
  outStream << (qint32)Htr::Net::ClResourceMsg;
  outStream << id();
  outStream << (qint32)Htr::Net::MimeDataMsg;
  outStream << mMimeData; 
  emit sendBroadCast(outPaket); 
  emit changed(); 
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::commitMimeType()
{
  QByteArray  outPaket;
  QDataStream outStream(&outPaket,QIODevice::WriteOnly);
  HtrManagedSocket::setupStream(outStream);
  outStream << (qint32)Htr::Net::ClResourceMsg;
  outStream << id();
  outStream << (qint32)Htr::Net::MimeTypeMsg;
  outStream << mMimeType;
  emit sendBroadCast(outPaket); 
  emit changed(); 
}

/*--------------------------------------------------------------------
*/
void HtrsMimeDataResource::store()
{
  if (!mIsPersistent)
    return;

  if (HtrsPlugins::instance()->persistentService())
    HtrsPlugins::instance()->persistentService()->store(this);
}
